import * as vscode from 'vscode';
import { executeCommandInChildProcess } from './command';
import * as path from 'path';

class TopicInfo {
    type: string;
    pub: number;
    sub: number;
    constructor(type: string, pub: number, sub: number) {
        this.type = type;
        this.pub = pub;
        this.sub = sub;
    }
}

async function GetAllTopics(): Promise<string[]> {
    const topicInfos = await executeCommandInChildProcess('ros2 topic list');
    const topics = topicInfos.stdout.split('\n').filter(line => line.length > 0);
    return topics;
}

async function GetTopicInfo(topicName: string): Promise<TopicInfo> {
    const topicInfo = await executeCommandInChildProcess(`ros2 topic info ${topicName}`);
    const type = topicInfo.stdout.split('\n')[0].split(':')[1].trim();
    const pubCount = parseInt(topicInfo.stdout.split('\n')[1].split(':')[1].trim());
    const subCount = parseInt(topicInfo.stdout.split('\n')[2].split(':')[1].trim());
    return new TopicInfo(type, pubCount, subCount);
}

async function GetAllNodes(): Promise<string[]> {
    const nodeInfos = await executeCommandInChildProcess('ros2 node list');
    const nodes = nodeInfos.stdout.split('\n').filter(line => line.length > 0);
    return nodes;
}

async function GetAllServices(): Promise<string[]> {
    const serviceInfos = await executeCommandInChildProcess('ros2 service list');
    const services = serviceInfos.stdout.split('\n').filter(line => line.length > 0);
    return services;
}
async function GetAllActions(): Promise<string[]> {
    const serviceInfos = await executeCommandInChildProcess('ros2 action list');
    const services = serviceInfos.stdout.split('\n').filter(line => line.length > 0);
    return services;
}

class MonitorItem extends vscode.TreeItem {
    constructor(name: string, collapsibleState: vscode.TreeItemCollapsibleState, contextValue?: string, iconPath?: string, description?: string) {
        super(name, collapsibleState);
        this.contextValue = contextValue;
        this.iconPath = iconPath;
        this.description = description;
    }
}

export enum MonitorItemType {
    Topic,
    Service,
    Action,
    Node
}

export class ClassItem extends MonitorItem {
    type: MonitorItemType;
    children: MonitorItem[] = [];
    constructor(name: string, type: MonitorItemType, children: MonitorItem[] = []) {
        let contextValue: string;
        switch (type) {
            case MonitorItemType.Topic:
                contextValue = "topicClassItem";
                break;
            case MonitorItemType.Service:
                contextValue = "serviceClassItem";
                break;
            case MonitorItemType.Action:
                contextValue = "actionClassItem";
                break;
            case MonitorItemType.Node:
                contextValue = "nodeClassItem";
                break;
        }
        super(name, vscode.TreeItemCollapsibleState.Collapsed, contextValue);
        this.type = type;
        this.children = children;
    }
}

class TopicItem extends MonitorItem {
    constructor(name: string, collapsibleState: vscode.TreeItemCollapsibleState = vscode.TreeItemCollapsibleState.None, info: TopicInfo) {
        super(name, collapsibleState, "topicItem", path.join(__dirname, '../resource/topic.png'), info.type);
    }
}

class NodeItem extends MonitorItem {
    constructor(name: string, collapsibleState: vscode.TreeItemCollapsibleState = vscode.TreeItemCollapsibleState.None) {
        super(name, collapsibleState, "nodeItem", path.join(__dirname, '../resource/node.png'));
    }
}

class ServiceItem extends MonitorItem {
    constructor(name: string, collapsibleState: vscode.TreeItemCollapsibleState = vscode.TreeItemCollapsibleState.None) {
        super(name, collapsibleState, "serviceItem", path.join(__dirname, '../resource/service.png'));
    }
}

class ActionItem extends MonitorItem {
    constructor(name: string, collapsibleState: vscode.TreeItemCollapsibleState = vscode.TreeItemCollapsibleState.None) {
        super(name, collapsibleState, "actionItem", path.join(__dirname, '../resource/action.png'));
    }
}

class PlaceHolderItem extends MonitorItem {
    constructor() {
        super('empty', vscode.TreeItemCollapsibleState.None, "placeholder");
    }
}

export class MonitorProvider implements vscode.TreeDataProvider<MonitorItem> {
    private _onDidChangeTreeData: vscode.EventEmitter<MonitorItem | undefined | null> = new vscode.EventEmitter<MonitorItem | undefined | null>();
    readonly onDidChangeTreeData: vscode.Event<MonitorItem | undefined | null> = this._onDidChangeTreeData.event;
    classRoots: MonitorItem[] = [
        new ClassItem('Topics', MonitorItemType.Topic),
        new ClassItem('Services', MonitorItemType.Service),
        new ClassItem('Actions', MonitorItemType.Action),
        new ClassItem('Nodes', MonitorItemType.Node)
    ];

    refresh(): void {
        console.log('refresh monitorTree');
        this._onDidChangeTreeData.fire(undefined);
    }

    getTreeItem(element: MonitorItem): vscode.TreeItem {
        return element;
    }

    getChildren(element?: MonitorItem): Thenable<MonitorItem[]> {
        if (!element) {
            return Promise.resolve(this.classRoots);
        }
        else if (element instanceof ClassItem) {
            switch (element.type) {
                case MonitorItemType.Topic:
                    return scanAllTopics();
                case MonitorItemType.Service:
                    return scanAllServices();
                case MonitorItemType.Action:
                    return scanAllActions();
                case MonitorItemType.Node:
                    return scanAllNodes();
            }
        }
        return Promise.resolve([]);
    }
}

async function scanAllTopics(): Promise<MonitorItem[]> {
    const topics = await GetAllTopics();
    if (topics.length === 0)
        return Promise.resolve([new PlaceHolderItem()]);
    const items = await topics.map(async topic => {
        const topicInfo = await GetTopicInfo(topic);
        return new TopicItem(topic, vscode.TreeItemCollapsibleState.None, topicInfo);
    });
    return Promise.all(items);
}
async function scanAllServices(): Promise<MonitorItem[]> {
    const services = await GetAllServices();
    const items = services.map(service => new ServiceItem(service, vscode.TreeItemCollapsibleState.None));
    if (items.length === 0)
        return Promise.resolve([new PlaceHolderItem()]);
    return Promise.resolve(items);
}

async function scanAllActions(): Promise<MonitorItem[]> {
    const actions = await GetAllActions();
    const items = actions.map(action => new ActionItem(action, vscode.TreeItemCollapsibleState.None));
    if (items.length === 0)
        return Promise.resolve([new PlaceHolderItem()]);
    return Promise.resolve(items);
}

async function scanAllNodes(): Promise<MonitorItem[]> {
    const nodes = await GetAllNodes();
    const items = nodes.map(node => { return new NodeItem(node, vscode.TreeItemCollapsibleState.None) });
    if (items.length === 0)
        return Promise.resolve([new PlaceHolderItem()]);
    return Promise.resolve(items);
}

